Home | | Latest | About | Random
# Week 5 Wednesday
Dear Class,
Good job on finishing Exam 1! Remember to keep a positive learning attitude, and focus on learning, finding what works and what doesn't, and go from there. Make tiny progress everyday. We'll probably finish grading by next Monday.
A "Pre-HW4" is posted, that should build you up to HW4 to come later. No need to turn in Pre-HW4 but it is good to do.
Today we start section 10, **references**, and tie up a few loose ends from section 9 about functions.
- Matching arguments to parameters
- Functions work on **copies** of their arguments when passing by value
- Then... **references**! (section 10)
- **Passing by value** vs **passing by reference**.
- One call, one return value
- Function comments
- Build error, undefined behavior, or success? A quiz!
For your own reading, please go through **9.5** (`main` is a function too!), **9.6** (reviewing syntax) and **9.7** (reviewing function calls) in the text, and try the **review questions in 9.8**. And anything we don't get to cover in class, please read the notes and in the text!
-- B.
.
.
.
.
.
## Who goes where?
Last time we had `bool is_triangle(double a, double b, double c)`, and we asked what this does:
```cpp
double a = 3;
double b = 4;
double c = 5;
cout << is_triangle(c, a, b);
```
.
.
.
.
.
Remember, when a function is called, a **function scope** is created, and the **parameters are initialized with the arguments in order**: the first argument goes to the first parameter, the second argument to the second parameter, and so on. **The names do not matter!**
So inside the function scope of `is_triangle`, what happens is like
```cpp
double a = 5; // first parameter a gets first argument c (which is 5)
double b = 3; // second parameter b gets second argument a (which is 3)
double c = 4; // third parameter c gets third argument b (which is 4)
```
The `a`, `b`, `c` inside the function are **different variables** from the `a`, `b`, `c` in `main`. They just happen to have the same names. (Draw the dashed box for the function scope, next to the box for `main`!)
.
.
.
.
.
## Let's write our own swap! But will it swap??
On HW3 you may have used `std::swap` from ``. Let's write our own!
```cpp
#include
using namespace std;
void my_swap(int a, int b);
int main() {
int a = 6;
int b = 7;
cout << a << b << endl;
my_swap(b, a);
cout << a << b << endl;
return 0;
}
void my_swap(int a, int b) {
if (a == b) {
return; // nothing to do, end the function call early!
}
int tmp = a; // tmp is a local variable of this function scope
a = b;
b = tmp;
}
```
What is the output?
.
.
.
.
.
```
67
67
```
**Huh?? Nothing got swapped!**
Let's trace through what happened when we call `my_swap(b, a)`:
- A function scope is created.
- The parameters are initialized with the arguments **in order**: like `int a = 7;` (from main's `b`) and `int b = 6;` (from main's `a`).
- `a == b` is `false`, so we don't return early.
- `tmp` is declared inside the function scope, and the **function's** `a` and `b` are swapped. So now the function's `a` is `6` and the function's `b` is `7`.
- We reach the closing brace `}`, the function ends, and the function scope is **destroyed**, with its `a`, `b`, and `tmp`.
The `a` and `b` back in `main` were never touched! **With parameters like `int a`, a function works on copies of its arguments.** Changing a parameter does not change the variable you passed in. This is called **passing by value**: only the *value* of the argument is handed to the function, not the variable itself.
**Remark.** Notice that `return;` can be used in a `void` function to end the call early. It does not return any value. And if a `void` function reaches its closing brace `}`, there is a hidden `return;` there, so the function ends.
**Remark.** `void` is a type whose set of values is empty! So you cannot declare a variable like `void x;`.
**Remark.** `std::swap` is also a `void` function, it returns nothing. So writing `a = swap(a, b);` is a **build error**, there is no value to assign to `a`!
So... how does `std::swap` manage to change `a` and `b` in `main`, while returning nothing at all?? Let's find out!
.
.
.
.
.
## References. (`&` "the arrow")
**References** are the answer!
Inside `main`, we can write
```cpp
int i = 0;
int& r = i;
```
The type of `r` is **not** `int`, it is `int&`, read aloud as "int ref". It means "a **reference** to an `int`".
Recall that a variable name is a label for a storage location in memory (on most machines an `int` takes up 4 bytes and a `double` 8 bytes; a `char` is always 1 byte). A **reference does not store its own data**. Instead, it **refers to data that already exists**.
We draw a reference as an **arrow** pointing to the box it refers to. (Draw a box `i` with `0` in it, and an arrow from `r` to that box!)
![[1-teaching/ucla-2026-fall-pic-10a/---drafts/---files/week-5-wednesday-draft 2026-10-28 04.20.56.excalidraw.svg]]
%%[[1-teaching/ucla-2026-fall-pic-10a/---drafts/---files/week-5-wednesday-draft 2026-10-28 04.20.56.excalidraw.md|🖋 Edit in Excalidraw]]%%
.
.
.
.
.
### Rules of references.
- **A reference must be initialized** when it is declared. `int& r;` is a **build error**. What would it point to?
- **A reference like `int&` must refer to an existing storage location of that type**, like an `int` variable. `int& r = 4;` is a **build error**: the literal `4` has no storage location for `r` to point to. (On Friday we will see references to `const`, like `const int&`, which follow a more relaxed rule.)
- **Once initialized, a reference refers to the same storage location forever.** The arrow always starts and ends at the same place. (Kind of like how a `const` variable never changes its value.)
So after `int& r = i;`, **any line involving `r` behaves as if we wrote `i` instead.** Follow the arrow!
.
.
.
.
.
### Follow the arrow! And the arrow never change!
What does this print? Predict each line before running it!
```cpp
#include
using namespace std;
int main() {
int i = 0;
int& r = i;
cout << i << ' ' << r << endl;
i = 1;
cout << i << ' ' << r << endl;
r = 2;
cout << i << ' ' << r << endl;
int j = 3;
r = j; // careful!
cout << i << ' ' << r << ' ' << j << endl;
i = 4;
cout << i << ' ' << r << ' ' << j << endl;
j = 5;
cout << i << ' ' << r << ' ' << j << endl;
return 0;
}
```
.
.
.
.
.
```
0 0
1 1
2 2
3 3 3
4 4 3
4 4 5
```
The tricky line is `r = j;`. It does **not** make `r` refer to `j`! The arrow from `r` still points at `i`, so `r = j;` is the same as `i = j;`, which puts `3` into `i`. After that, `j` is its own variable, and changing `j` does nothing to `i` or `r`.
.
.
.
.
.
### Back to swap!
Let's fix our `my_swap`. All we change is **two ampersands**:
```cpp
#include
using namespace std;
void good_swap(int& a, int& b);
int main() {
int i = 0;
int j = 1;
cout << i << ' ' << j << endl;
good_swap(i, j);
cout << i << ' ' << j << endl;
return 0;
}
void good_swap(int& a, int& b) {
int tmp = a;
a = b;
b = tmp;
}
```
Output:
```
0 1
1 0
```
**It works!** Let's trace through `good_swap(i, j)`:
- A function scope is created.
- The parameters are initialized with the arguments, in order. But now the parameters are references, so it is like `int& a = i;` and `int& b = j;`. (This is why we say *initialized*, not *assigned*: initializing a reference sets where its arrow points, while assigning to a reference later, like `a = b;` below, writes through the arrow.) (Draw the dashed box, with arrows from `a` to main's `i` and from `b` to main's `j`!)
- `int tmp = a;` puts a copy of `i`'s value `0` into `tmp`. Note `tmp` is a plain `int`, its own box.
- `a = b;` follows both arrows, so it is like `i = j;`. Now `i` is `1`.
- `b = tmp;` follows the arrow, so it is like `j = tmp;`. Now `j` is `0`.
- The function ends, and the function scope is destroyed: `a`, `b`, `tmp` are gone. But `a` and `b` never had their own data. **The changes live on in `main`'s `i` and `j`!**
This is called **passing by reference**: the parameters refer to the caller's variables themselves, instead of getting copies. Compare with `my_swap`, which **passed by value**. (We will say much more about choosing between the two next week.)
**Remark.** `good_swap` is still a procedure, it returns nothing. Its side effect is changing the variables passed in. So with references, a function can "give back" as many results as we like, by changing variables we pass in.
**Remark.** `good_swap(0, 1);` is a **build error**. Why? (Hint: look at the rules of references above.)
**Remark.** `std::swap` is still better than our `good_swap` in a number of ways, which you will see in PIC 10B and 10C.
.
.
.
.
.
### Try it!
What does this print?
```cpp
#include
using namespace std;
void f(int& a, int b) {
++a;
++b;
cout << a << ' ' << b << endl;
}
int main() {
int i = 0;
int j = 0;
f(i, j);
cout << i << ' ' << j << endl;
return 0;
}
```
Draw the function scope with its arrows!
.
.
.
.
.
Answer:
```
1 1
1 0
```
.
.
.
.
.
**We ended here in class. Please read rest of the following on your own for some loose ends with functions, also please read the book for more details.**
## One call, one return value.
Back to functions for a few loose ends. **A single function call never returns two values.** As soon as a `return` is reached, the function call ends. So something like
```cpp
int f(int x) {
return x;
return x + 1; // never reached!
}
```
only ever gives back `x`.
But with references, we now have a way around this! A function can "give back" extra results by changing variables passed in by reference, just like `good_swap` changed both `i` and `j` without returning anything.
.
.
.
.
.
## Function comments.
From now on, we write a **function comment** for each function we write (other than `main`). It is written inside `/**` and `*/`, and has:
- A description of what the function does.
- `@param name : description` for each parameter.
- `@return description` of what is returned, if the return type is not `void`.
It should be enough so someone can use your function **without reading its body**.
```cpp
/**
Checks whether three side lengths can form a triangle.
@param a : the first side length
@param b : the second side length
@param c : the third side length
@return true if a, b, c can form a triangle, false otherwise
*/
bool is_triangle(double a, double b, double c);
```
Try writing one for `good_swap`! (No `@return` needed, why?)
.
.
.
.
.
## Build error, undefined behavior, or success?
For each program below, is it a **build error**, a **runtime error**, **undefined behavior**, or **success**? Try it and vote!
### (1)
```cpp
#include
using namespace std;
int main() {
cout << square(3) << endl;
return 0;
}
double square(double x) {
return x * x;
}
```
### (2)
```cpp
#include
using namespace std;
double square(double x);
int main() {
cout << square(3) << endl;
return 0;
}
```
### (3)
```cpp
#include
using namespace std;
double square(double x);
double square(double y);
double square(double);
int main() {
cout << square(3) << endl;
return 0;
}
double square(double x) {
return x * x;
}
```
### (4)
```cpp
#include
using namespace std;
double square(double x);
int main() {
cout << square(3) << endl;
return 0;
}
double square(double x) {
return x * x;
}
double square(double x) {
return x * x;
}
```
### (5)
```cpp
#include
using namespace std;
bool is_triangle(double a, double b, double c);
int main() {
bool q = is_triangle(1, 1, 100);
cout << q << endl;
return 0;
}
bool is_triangle(double a, double b, double c) {
if ((a + b > c) && (a + c > b) && (b + c > a)) {
return true;
}
}
```
.
.
.
.
.
### Answers.
1. **Build error.** `square` is called before it is declared. (The definition is after `main`, too late!)
2. **Build error.** `square` is declared but never defined. This is a special kind of build error called a **linking error**: the compiler is happy, but when the pieces get put together, the definition of `square` is nowhere to be found.
3. **Success!** You may **declare** a function as many times as you like, and parameter names in a declaration are optional and don't even need to match.
4. **Build error.** You may only **define** a function once.
5. **Undefined behavior.** Since `1, 1, 100` is not a triangle, the function reaches its closing brace `}` without ever hitting a `return`. If a non-`void` function (other than `main`) does that, the behavior is undefined, even if we never use the returned value. **Make sure every path through a non-`void` function returns something!** (The fix: add `return false;` at the end, or the `else` like we had on Friday.)
**Remark.** Here is an even better fix. The condition `(a + b > c) && (a + c > b) && (b + c > a)` is **already** a `bool`, either `true` or `false`. So why not just return it?
```cpp
bool is_triangle(double a, double b, double c) {
return (a + b > c) && (a + c > b) && (b + c > a);
}
```
One line, no `if`, and every path returns something! (Hint: try this on `leap_year` in PHW4.)
---